applicationContext.xml 解析详细-Spring源码学习之XML文件的初解析

首先,在我的这篇博客中已经说到容器是怎么初步实现的,并且要使用XmlBeanDefinitionReader对象对Xml文件进行解析,那么Xml文件是如何进行解析的,将在这片博客中进行一些陈述.


数据准备阶段

准备的目的是封装resource参数,目的是为了考虑到Resource可能存在编码要求的情况,其次,通过SAX读取XML文件的方式来准备InputSource对象,最后将参数传递到最核心的实现部分doLoadBeanDefinitions(inputSource,encodedResource.getResource())

封装Resource

调用XmlBeanDefinitionReaderloadBeanDefinitions(Resource resource)方法时,首先将resource对象进行再次封装成EncodedResource,查看源码可以发现里面增加了字符集和编码的封装,从命名上来看也可以体现出来,将资源封装完成后,就调用重载的同名函数loadBeanDefinitions(EncodedResource resource)进行正式的解析.

数据准备操作

在重载方法里面首先通过Set<!-- <EncodedResource> --> currentResources属性来记录已经加载的资源,其次,从EncodedResource对象中获取封装好的Resource对象,并获取其inputStream,将获取到的输入流与SAX解析的InputSource绑定,接下来就进入到了核心的实现部分:doLoadBeanDefinitions(inputSource,encodedResource.getResource())

核心实现

核心部分有两个关键步骤:

  1. 调用doLoadDocument(inputSource.resource)方法获取Document
  2. 根据返回的Document信息注册Bean信息

这两个步骤支持着整个Spring容器部分的实现基础

获取Document

进入方法体后,将Document的创建交给DefaultDocumentLoader documentLoader属性的loadDocument()方法,该方法声明如下:

1
2
3
4
Document loadDocument(
InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
throws Exception;

调用情况:

1
2
documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware())

  • InputSource:SAX解析需要使用到的对象

  • EntityResolver:它的作用是项目本身就可以提供一个如何寻找DTD声明的方法,由程序来实现寻找DTD声明的过程,将DTD文件放到项目中某处,在实现时直接将此文档读取并返回给SAX即可,避免了必须通过网络来寻找相应的声明.

在这个接口中定义了一个方法

1
2
InputSource resolveEntity (String publicId,String systemId)
throws SAXException, IOException;

如果解析的验证模式是XSD:

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd>

那么.此时得到的两个参数值分别是:

publicId:null
systemId:http://www.springframework.org/schema/beans/spring-beans.xsd

如果解析的验证模式是DTD:

1
2
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN 2.0//EN" "http://www.Springframework.org/dtd/Spring-beans-2.0.dtd"

那么,此时得到的两个参数值分别是:

publicId:-//Spring//DTD BEAN 2.0//EN
systemId:http://www.Springframework.org/dtd/Spring-beans-2.0.dtd

而对于不同的验证模式,Spring使用了不同的解析器,当使用DTD验证时,Spring会截取后面的*.dtd,并直接到当前目录去寻找,当使用XSD验证时,Spring会到META-INF/Spring.schemas文件中去匹配相应的systemId并加载对应的XSD文件

  • validationMode:验证模式

首先,为了保证XML文件的正确性,有常见两种验证模式:DTDXSD

两种验证模式的区别

我对这两种的区别目前还不是很详细,只能简略的给出定义,但我看到的最直观的区别是,DTD验证需要单独写出一个标签<!DOCTYPE ...>,而XSD验证会将信息写入<beans xmlns="...">结点

DTD

DTD(Document Type Definition)即文档类型定义,是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范.

XSD

XML Schema语言就是XSD(XML Schema Definition),描述了XML文档的结构,可以用一个指定的XML Schema来验证XML文档,以检查文档是否符合要求.

验证模式的读取

验证模式的读取非常简单,在getValidationModeForResource(resource)方法中先获取当前设定的验证模式是不是自动选择,源码中是这么解释的since we cannot find a clear indication,当找不到一个确切的验证模式时,采用这种方式,然后判断当前resource对象中采用的是什么验证模式,通过检索字符串的方式,当存在DOCTYPE的时候,就采用DTD验证模式,否则采用XSD验证模式

  • namespaceAware:一个布尔值,默认为false,在前面可以看到,在使用XSD验证的时候会有xmlns="",其实就是XML namespace的缩写,可以有多个命名空间,如果使用的是XSD解析,将会把这个值改为true

解析并注册BeanDefinitions

在上一步得到Docment对象之后,调用registerBeanDefinitions(Document doc,Resource resource)

1
2
3
4
5
6
7
8
9
10
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建对象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//记录当前已经加载的数量
int countBefore = getRegistry().getBeanDefinitionCount();
//加载并注册
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回本次加载的个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}

而在调用documentReader对象方法中,才开始进行正式的解析工作

1
2
3
4
5
6
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}

解析的工作全权交给doRegisterBeanDefinition(root)方法实现,这样XML文件就正式进入了解析步骤,至于怎么解析的,博主将慢慢学习并写入后续博客.

文章目录
  1. 1. 数据准备阶段
    1. 1.1. 封装Resource
    2. 1.2. 数据准备操作
  2. 2. 核心实现
    1. 2.1. 获取Document
      1. 2.1.1. 两种验证模式的区别
        1. 2.1.1.1. DTD
        2. 2.1.1.2. XSD
      2. 2.1.2. 验证模式的读取
    2. 2.2. 解析并注册BeanDefinitions
|